﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

#if NETFX_CORE
using Windows.ApplicationModel.Resources;

#endif

namespace StyleMVVM.Validation.Impl.Fluent
{
	public class FluentIntProperty<T> : IIntPropertyOperator<T>, IFluentPropertyValueProvider<T>
	{
		private enum ModifierType
		{
			Add,
			Subtract,
			Multiply,
			Divide,
			Mod
		}

		private readonly IFluentRule<T> fluentRule;
		private ModifierType modifier;
		private int modifierValue;
		private static MethodInfo addMethod;
		private static MethodInfo subtractMethod;
		private static MethodInfo multiplyMethod;
		private static MethodInfo divideMethod;
		private static MethodInfo modMethod;

		static FluentIntProperty()
		{
			addMethod = typeof(FluentIntProperty<object>).GetTypeInfo().GetDeclaredMethod("AddObject");

			subtractMethod = typeof(FluentIntProperty<object>).GetTypeInfo().GetDeclaredMethod("SubtractObject");

			multiplyMethod = typeof(FluentIntProperty<object>).GetTypeInfo().GetDeclaredMethod("MultiplyObject");

			divideMethod = typeof(FluentIntProperty<object>).GetTypeInfo().GetDeclaredMethod("DivideObject");

			modMethod = typeof(FluentIntProperty<object>).GetTypeInfo().GetDeclaredMethod("ModuleObject");
		}

		public FluentIntProperty(IFluentRule<T> fluentRule, string propertyName)
		{
			this.fluentRule = fluentRule;
			PropertyName = propertyName;
		}

		public IFluentRule<T> Plus(int x)
		{
			modifier = ModifierType.Add;

			modifierValue = x;

			return fluentRule;
		}

		public IFluentRule<T> Minus(int x)
		{
			modifier = ModifierType.Subtract;

			modifierValue = x;

			return fluentRule;
		}

		public IFluentRule<T> Multiply(int x)
		{
			modifier = ModifierType.Multiply;

			modifierValue = x;

			return fluentRule;
		}

		public IFluentRule<T> Divide(int x)
		{
			modifier = ModifierType.Divide;

			modifierValue = x;

			return fluentRule;
		}

		public IFluentRule<T> Mod(int x)
		{
			modifier = ModifierType.Mod;

			modifierValue = x;

			return fluentRule;
		}

		public string PropertyName { get; private set; }

		public Expression GenerateExpression(IFluentExpressionProvider propertyExpressionProvider)
		{
			Expression returnValue = null;

			switch (modifier)
			{
				case ModifierType.Add:
					returnValue =
						Expression.Call(addMethod,
						                propertyExpressionProvider.GetPropertyExpression(PropertyName),
						                Expression.Constant(modifierValue));

					break;
				case ModifierType.Subtract:
					returnValue =
						Expression.Call(subtractMethod,
						                propertyExpressionProvider.GetPropertyExpression(PropertyName),
						                Expression.Constant(modifierValue));

					break;
				case ModifierType.Multiply:
					returnValue =
						Expression.Call(multiplyMethod,
						                propertyExpressionProvider.GetPropertyExpression(PropertyName),
						                Expression.Constant(modifierValue));

					break;
				case ModifierType.Divide:
					returnValue =
						Expression.Call(divideMethod,
						                propertyExpressionProvider.GetPropertyExpression(PropertyName),
						                Expression.Constant(modifierValue));
					break;
				case ModifierType.Mod:
					returnValue =
						Expression.Call(modMethod,
						                propertyExpressionProvider.GetPropertyExpression(PropertyName),
						                Expression.Constant(modifierValue));

					break;
			}

			return returnValue;
		}

		public string GenerateDisplayString(ResourceLoader loader)
		{
			string returnValue = null;

#if NETFX_CORE
			string formatString;

			switch (modifier)
			{
				case ModifierType.Add:
					formatString = loader.GetString("Plus");

					returnValue = string.Format(formatString, PropertyName, modifierValue);
					break;
				case ModifierType.Subtract:
					formatString = loader.GetString("Minus");

					returnValue = string.Format(formatString, PropertyName, modifierValue);
					break;
				case ModifierType.Multiply:
					formatString = loader.GetString("Times");

					returnValue = string.Format(formatString, PropertyName, modifierValue);
					break;
				case ModifierType.Divide:
					formatString = loader.GetString("DividedBy");

					returnValue = string.Format(formatString, PropertyName, modifierValue);
					break;
				case ModifierType.Mod:
					formatString = loader.GetString("Modules");

					returnValue = string.Format(formatString, PropertyName, modifierValue);
					break;
			}
#else
			switch (modifier)
			{
				case ModifierType.Add:
					returnValue = string.Format(FluentResources.Plus, PropertyName, modifierValue);
					break;
				case ModifierType.Subtract:
					returnValue = string.Format(FluentResources.Minus, PropertyName, modifierValue);
					break;
				case ModifierType.Multiply:
					returnValue = string.Format(FluentResources.Times, PropertyName, modifierValue);
					break;
				case ModifierType.Divide:
					returnValue = string.Format(FluentResources.DividedBy, PropertyName, modifierValue);
					break;
				case ModifierType.Mod:
					returnValue = string.Format(FluentResources.Modules, PropertyName, modifierValue);
					break;
			}
#endif

			return returnValue;
		}

		private static object AddObject(object target, int addValue)
		{
			if (target is int)
			{
				return (int)target + addValue;
			}

			return null;
		}

		private static object SubtractObject(object target, int addValue)
		{
			if (target is int)
			{
				return (int)target - addValue;
			}

			return null;
		}

		private static object MultiplyObject(object target, int addValue)
		{
			if (target is int)
			{
				return (int)target * addValue;
			}

			return null;
		}

		private static object DivideObject(object target, int addValue)
		{
			if (target is int)
			{
				return (int)target / addValue;
			}

			return null;
		}

		private static object ModuleObject(object target, int addValue)
		{
			if (target is int)
			{
				return (int)target % addValue;
			}

			return null;
		}
	}
}